/* s_fileio.c
 *
 * Micropolis, Unix Version.  This game was released for the Unix platform
 * in or about 1990 and has been modified for inclusion in the One Laptop
 * Per Child program.  Copyright (C) 1989 - 2007 Electronic Arts Inc.  If
 * you need assistance with this program, you may contact:
 *   http://wiki.laptop.org/go/Micropolis  or email  micropolis@laptop.org.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.  You should have received a
 * copy of the GNU General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 * 
 *             ADDITIONAL TERMS per GNU GPL Section 7
 * 
 * No trademark or publicity rights are granted.  This license does NOT
 * give you any right, title or interest in the trademark SimCity or any
 * other Electronic Arts trademark.  You may not distribute any
 * modification of this program using the trademark SimCity or claim any
 * affliation or association with Electronic Arts Inc. or its employees.
 * 
 * Any propagation or conveyance of this program must include this
 * copyright notice and these terms.
 * 
 * If you convey this program (or any modifications of it) and assume
 * contractual liability for the program to recipients of it, you agree
 * to indemnify Electronic Arts for any liability that those contractual
 * assumptions impose on Electronic Arts.
 * 
 * You may not misrepresent the origins of this program; modified
 * versions of the program must be marked as such and not identified as
 * the original program.
 * 
 * This disclaimer supplements the one included in the General Public
 * License.  TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
 * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
 * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK.  THE ENTIRE RISK OF
 * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU.  ELECTRONIC ARTS
 * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
 * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
 * USAGE, OR TRADE PRACTICE.  ELECTRONIC ARTS DOES NOT WARRANT AGAINST
 * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
 * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
 * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
 * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
 * CORRECTED.  NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
 * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY.  SOME
 * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
 * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
 * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
 * NOT APPLY TO YOU.
 */
#include "sim.h"

void DoSaveCityAs(void);
void DidSaveCity(void);
void DidntSaveCity(char *msg);
void DidLoadCity(void);
void DidntLoadCity(char *msg);
void DidLoadScenario(void);

#define SWAP_SHORTS(a,b)	_swap_shorts(a,b)
#define SWAP_LONGS(a,b)		_swap_longs(a,b)
#define HALF_SWAP_LONGS(a,b)	_half_swap_longs(a,b)

#define NOOP_ON_BE	{ int test = 1; if (!(*(unsigned char*) (&test))) return; }

static void _swap_shorts(short *buf, int len)
{
    int i;

    NOOP_ON_BE;

    /*
     * Flip bytes in each short! 
     */
    for(i = 0; i < len; i++)
    {
        *buf = ((*buf & 0xFF) << 8) | ((*buf & 0xFF00) >> 8);
        buf++;
    }
}

#if 0
static void _swap_longs(long *buf, int len)
{
    int i;

    NOOP_ON_BE;

    /*
     * Flip bytes in each long! 
     */
    for(i = 0; i < len; i++)
    {
        long l = *buf;

        *buf = ((l & 0x000000ff) << 24) | ((l & 0x0000ff00) << 8) | ((l & 0x00ff0000) >> 8) | ((l & 0xff000000) >> 24);
        buf++;
    }
}
#endif

static void _half_swap_longs(long *buf, int len)
{
    int i;

    NOOP_ON_BE
        /*
         * Flip bytes in each long! 
         */
        for(i = 0; i < len; i++)
    {
        long l = *buf;

        *buf = ((l & 0x0000ffff) << 16) | ((l & 0xffff0000) >> 16);
        buf++;
    }
}

static int _load_short(short *buf, int len, FILE * f)
{
    if(fread(buf, sizeof(short), len, f) != len)
        return 0;

    SWAP_SHORTS(buf, len);      /* to intel */

    return 1;
}

#if 0
static int _load_long(long *buf, int len, FILE * f)
{
    if(fread(buf, sizeof(long), len, f) != len)
        return 0;

    SWAP_LONGS(buf, len);       /* to intel */

    return 1;
}
#endif

static int _save_short(short *buf, int len, FILE * f)
{

    SWAP_SHORTS(buf, len);      /* to MAC */

    if(fwrite(buf, sizeof(short), len, f) != len)
        return 0;

    SWAP_SHORTS(buf, len);      /* back to intel */

    return 1;
}

#if 0
static int _save_long(long *buf, int len, FILE * f)
{

    SWAP_LONGS(buf, len);       /* to MAC */

    if(fwrite(buf, sizeof(long), len, f) != len)
        return 0;

    SWAP_LONGS(buf, len);       /* back to intel */

    return 1;
}
#endif

static int _load_file(char *filename, char *dir)
{
    FILE *f;
    char path[512];
    QUAD size;

#ifdef MSDOS
    if(dir != NULL)
    {
        sprintf(path, "%s\\%s", dir, filename);
        filename = path;
    }
    if((f = fopen(filename, "rb")) == NULL)
    {
        return 0;
    }
#else
    if(dir != NULL)
    {
        sprintf(path, "%s/%s", dir, filename);
        filename = path;
    }
    if((f = fopen(filename, "r")) == NULL)
    {
        return (0);
    }
#endif

    fseek(f, 0L, SEEK_END);
    size = ftell(f);
    fseek(f, 0L, SEEK_SET);

    switch (size)
    {
        case 27120:            /* Normal city */
            break;

        case 99120:            /* 2x2 city */
            break;

        case 219120:           /* 3x3 city */
            break;

        default:
            return (0);
    }

    if((_load_short(ResHis, HISTLEN / 2, f) == 0) ||
       (_load_short(ComHis, HISTLEN / 2, f) == 0) ||
       (_load_short(IndHis, HISTLEN / 2, f) == 0) ||
       (_load_short(CrimeHis, HISTLEN / 2, f) == 0) ||
       (_load_short(PollutionHis, HISTLEN / 2, f) == 0) ||
       (_load_short(MoneyHis, HISTLEN / 2, f) == 0) ||
       (_load_short(MiscHis, MISCHISTLEN / 2, f) == 0) || (_load_short((&Map[0][0]), WORLD_X * WORLD_Y, f) < 0))
    {

        /*
         * TODO:  report error 
         */
        fclose(f);
        return (0);
    }

    fclose(f);
    return (1);
}

int loadFile(char *filename)
{
    long l;

    if(_load_file(filename, NULL) == 0)
        return (0);

    /*
     * total funds is a long.....    MiscHis is array of shorts 
     */
    /*
     * total funds is being put in the 50th & 51th word of MiscHis 
     */
    /*
     * find the address, cast the ptr to a lontPtr, take contents 
     */

    l = *(QUAD *) (MiscHis + 50);
    HALF_SWAP_LONGS(&l, 1);
    SetFunds(l);

    l = *(QUAD *) (MiscHis + 8);
    HALF_SWAP_LONGS(&l, 1);
    CityTime = l;

    autoBulldoze = MiscHis[52]; /* flag for autoBulldoze */
    autoBudget = MiscHis[53];   /* flag for autoBudget */
    autoGo = MiscHis[54];       /* flag for autoGo */
    UserSoundOn = MiscHis[55];  /* flag for the sound on/off */
    CityTax = MiscHis[56];
    SimSpeed = MiscHis[57];
    //  sim_skips = sim_skip = 0;
    ChangeCensus();
    MustUpdateOptions = 1;

    /*
     * yayaya 
     */

    l = *(QUAD *) (MiscHis + 58);
    HALF_SWAP_LONGS(&l, 1);
    policePercent = l / 65536.0;

    l = *(QUAD *) (MiscHis + 60);
    HALF_SWAP_LONGS(&l, 1);
    firePercent = l / 65536.0;

    l = *(QUAD *) (MiscHis + 62);
    HALF_SWAP_LONGS(&l, 1);
    roadPercent = l / 65536.0;

    policePercent = (*(QUAD *) (MiscHis + 58)) / 65536.0;   /* and 59 */
    firePercent = (*(QUAD *) (MiscHis + 60)) / 65536.0; /* and 61 */
    roadPercent = (*(QUAD *) (MiscHis + 62)) / 65536.0; /* and 63 */

    if(CityTime < 0)
        CityTime = 0;
    if((CityTax > 20) || (CityTax < 0))
        CityTax = 7;
    if((SimSpeed < 0) || (SimSpeed > 3))
        SimSpeed = 3;

    setSpeed(SimSpeed);
    setSkips(0);

    InitFundingLevel();

    /*
     * set the scenario id to 0 
     */
    InitWillStuff();
    ScenarioID = 0;
    InitSimLoad = 1;
    DoInitialEval = 0;
    DoSimInit();
    InvalidateEditors();
    InvalidateMaps();

    return (1);
}

int saveFile(char *filename)
{
    long l;
    FILE *f;

#ifdef MSDOS
    if((f = fopen(filename, "wb")) == NULL)
    {
#else
    if((f = fopen(filename, "w")) == NULL)
    {
#endif
        /*
         * TODO: report error 
         */
        return (0);
    }

    /*
     * total funds is a long.....    MiscHis is array of ints 
     */
    /*
     * total funds is bien put in the 50th & 51th word of MiscHis 
     */
    /*
     * find the address, cast the ptr to a lontPtr, take contents 
     */

    l = TotalFunds;
    HALF_SWAP_LONGS(&l, 1);
    (*(QUAD *) (MiscHis + 50)) = l;

    l = CityTime;
    HALF_SWAP_LONGS(&l, 1);
    (*(QUAD *) (MiscHis + 8)) = l;

    MiscHis[52] = autoBulldoze; /* flag for autoBulldoze */
    MiscHis[53] = autoBudget;   /* flag for autoBudget */
    MiscHis[54] = autoGo;       /* flag for autoGo */
    MiscHis[55] = UserSoundOn;  /* flag for the sound on/off */
    MiscHis[57] = SimSpeed;
    MiscHis[56] = CityTax;      /* post release */

    /*
     * yayaya 
     */

    l = (int) (policePercent * 65536);
    HALF_SWAP_LONGS(&l, 1);
    (*(QUAD *) (MiscHis + 58)) = l;

    l = (int) (firePercent * 65536);
    HALF_SWAP_LONGS(&l, 1);
    (*(QUAD *) (MiscHis + 60)) = l;

    l = (int) (roadPercent * 65536);
    HALF_SWAP_LONGS(&l, 1);
    (*(QUAD *) (MiscHis + 62)) = l;

    if((_save_short(ResHis, HISTLEN / 2, f) == 0) ||
       (_save_short(ComHis, HISTLEN / 2, f) == 0) ||
       (_save_short(IndHis, HISTLEN / 2, f) == 0) ||
       (_save_short(CrimeHis, HISTLEN / 2, f) == 0) ||
       (_save_short(PollutionHis, HISTLEN / 2, f) == 0) ||
       (_save_short(MoneyHis, HISTLEN / 2, f) == 0) ||
       (_save_short(MiscHis, MISCHISTLEN / 2, f) == 0) || (_save_short((&Map[0][0]), WORLD_X * WORLD_Y, f) < 0))
    {

        /*
         * TODO:  report error 
         */
        fclose(f);
        return (0);
    }

    fclose(f);
    return (1);
}

void LoadScenario(short s)
{
    char *name = NULL, *fname = NULL;

    if(CityFileName != NULL)
    {
        ckfree(CityFileName);
        CityFileName = NULL;
    }

    if((s < 1) || (s > 8))
        s = 1;

    switch (s)
    {
        case 1:
            name = "Dullsville";
            fname = "snro.111";
            ScenarioID = 1;
            CityTime = ((1900 - 1900) * 48) + 2;
            SetFunds(5000);
            break;
        case 2:
            name = "San Francisco";
            fname = "snro.222";
            ScenarioID = 2;
            CityTime = ((1906 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
        case 3:
            name = "Hamburg";
            fname = "snro.333";
            ScenarioID = 3;
            CityTime = ((1944 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
        case 4:
            name = "Bern";
            fname = "snro.444";
            ScenarioID = 4;
            CityTime = ((1965 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
        case 5:
            name = "Tokyo";
            fname = "snro.555";
            ScenarioID = 5;
            CityTime = ((1957 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
        case 6:
            name = "Detroit";
            fname = "snro.666";
            ScenarioID = 6;
            CityTime = ((1972 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
        case 7:
            name = "Boston";
            fname = "snro.777";
            ScenarioID = 7;
            CityTime = ((2010 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
        case 8:
            name = "Rio de Janeiro";
            fname = "snro.888";
            ScenarioID = 8;
            CityTime = ((2047 - 1900) * 48) + 2;
            SetFunds(20000);
            break;
    }

    setAnyCityName(name);
    //  sim_skips = sim_skip = 0;
    InvalidateMaps();
    InvalidateEditors();
    setSpeed(3);
    CityTax = 7;
    gettimeofday(&start_time, NULL);

    _load_file(fname, ResourceDir);

    InitWillStuff();
    InitFundingLevel();
    UpdateFunds();
    InvalidateEditors();
    InvalidateMaps();
    InitSimLoad = 1;
    DoInitialEval = 0;
    DoSimInit();
    DidLoadScenario();
    Kick();
}

void DidLoadScenario(void)
{
    Eval("UIDidLoadScenario");
}

int LoadCity(char *filename)
{
    char *cp;
    char msg[256];

    if(loadFile(filename))
    {
        if(CityFileName != NULL)
            ckfree(CityFileName);
        CityFileName = (char *) ckalloc(strlen(filename) + 1);
        strcpy(CityFileName, filename);

        if((cp = (char *) rindex(filename, '.')))
            *cp = 0;
#ifdef MSDOS
        if((cp = (char *) rindex(filename, '\\')))
#else
        if((cp = (char *) rindex(filename, '/')))
#endif
            cp++;
        else
            cp = filename;
        filename = (char *) ckalloc(strlen(cp) + 1);
        strcpy(filename, cp);
        setCityName(filename);
        gettimeofday(&start_time, NULL);

        InvalidateMaps();
        InvalidateEditors();
        DidLoadCity();
        return (1);
    }
    else
    {
        sprintf(msg, "Unable to load a city from the file named \"%s\". %s",
                filename ? filename : "(null)", errno ? strerror(errno) : "");
        DidntLoadCity(msg);
        return (0);
    }
}

void DidLoadCity(void)
{
    Eval("UIDidLoadCity");
}

void DidntLoadCity(char *msg)
{
    char buf[1024];

    sprintf(buf, "UIDidntLoadCity {%s}", msg);
    Eval(buf);
}

void SaveCity(void)
{
    char msg[256];

    if(CityFileName == NULL)
    {
        DoSaveCityAs();
    }
    else
    {
        if(saveFile(CityFileName))
            DidSaveCity();
        else
        {
            sprintf(msg, "Unable to save the city to the file named \"%s\". %s",
                    CityFileName ? CityFileName : "(null)", errno ? strerror(errno) : "");
            DidntSaveCity(msg);
        }
    }
}

void DoSaveCityAs(void)
{
    Eval("UISaveCityAs");
}

void DidSaveCity(void)
{
    Eval("UIDidSaveCity");
}

void DidntSaveCity(char *msg)
{
    char buf[1024];

    sprintf(buf, "UIDidntSaveCity {%s}", msg);
    Eval(buf);
}

void SaveCityAs(char *filename)
{
    char msg[256];
    char *cp;

    if(CityFileName != NULL)
        ckfree(CityFileName);
    CityFileName = (char *) ckalloc(strlen(filename) + 1);
    strcpy(CityFileName, filename);

    if(saveFile(CityFileName))
    {
        if((cp = (char *) rindex(filename, '.')))
            *cp = 0;
#ifdef MSDOS
        if((cp = (char *) rindex(filename, '\\')))
#else
        if((cp = (char *) rindex(filename, '/')))
#endif
            cp++;
        else
            cp = filename;
        filename = (char *) ckalloc(strlen(cp) + 1);
        strcpy(filename, cp);
        setCityName(cp);
        DidSaveCity();
    }
    else
    {
        sprintf(msg, "Unable to save the city to the file named \"%s\". %s",
                CityFileName ? CityFileName : "(null)", errno ? strerror(errno) : "");
        DidntSaveCity(msg);
    }
}
